Skip to content

Obtainment methods for every Pokémon in every game - #5

Merged
codebend3r merged 25 commits into
mainfrom
obtainment-methods
Jul 31, 2026
Merged

Obtainment methods for every Pokémon in every game#5
codebend3r merged 25 commits into
mainfrom
obtainment-methods

Conversation

@codebend3r

Copy link
Copy Markdown
Owner

What

Every Pokémon's card gains a HOW TO OBTAIN section: per-game encounter locations with levels/rates/conditions, gifts, NPC trades, breeding info, and derived fallbacks (evolve / hatch / transfer) — grouped by region tabs (KANTOPALDEA, with HISUI · ANCIENT SINNOH under Sinnoh).

How

  • scripts/build-obtain-data.mts merges PokéAPI encounters (full Gen 1-7 detail), PokemonDB where-to-find tables (all games incl. Gen 8-9), and Bulbapedia in-game-trade lists into public/obtain/{id}.json (1,351 files, disk-cached + throttled scrape)
  • pure parse/merge modules in src/obtain/ (pokeapi, pokemondb, bulbapedia, assemble), unit-tested with real-structure fixtures
  • derived fallbacks are gated: no breeding advice in Gen 1/LGPE/PLA, dex-cut species read "Not in this game's Pokédex", no derived rows next to "Not available"
  • useObtainData lazy-fetches a file only when the section first opens; ObtainMethods renders region tabs, TYPE_COLORS-tinted method tags, icon+label condition chips (no raw slugs), and a collapsible legend
  • 26 new tests; full suite 130 passed / 2 pre-existing skips; tsc -b clean

Known limitations

  • evolve-only regional forms (e.g. Alolan Raichu) list only their wild/trade games — a per-form game map is future work
  • one cosmetic duplicate Bulbapedia trade row (Basculin, B/W)

- pipeline: PokéAPI + PokemonDB + Bulbapedia merged into `public/obtain/{id}.json`
- UI: `HOW TO OBTAIN` section in `PokemonCard.tsx`, lazy-fetched per Pokémon
- full encounter detail Gen 1-7, location lines Gen 8-9, derived fallbacks
- 10 TDD tasks: `src/obtain/` parse/merge modules, build script, dataset, UI wiring
- parser code grounded against live PokemonDB/Bulbapedia/PokéAPI structures
- covers derived fallbacks, version folding, lazy `public/obtain/{id}.json` fetch
- `src/obtain/types.ts`: `ObtainFile`/`ObtainGame`/`ObtainEntry` schema
- `VERSION_GROUP_VERSIONS`, `VERSION_TO_GROUP`, `GROUP_GEN` derived from `GENERATIONS`
- `isObtainFile` guard for runtime JSON validation
- `encountersToEntries` groups `/pokemon/{id}/encounters` by version
- merges same location+method+condition slots: level span, summed `chance`
- rod tier and unmapped method slugs preserved as `conditions`
- `parseWhereToFind` reads `table.vitals-table` rows keyed by `igame` slug
- location links become `wild` entries; `<small>` notes classify to `transfer`/`unavailable`/`evolve`/`egg`/`special`
- adds `node-html-parser` devDependency (script-side only)
- `parseTradeLists` walks mw-headline sections mapped in `TRADE_SECTIONS`
- give/receive species read in order from `(Pokémon)` title links per row
- `bulbaNameToSlug` normalizes names to PokéAPI slugs with overrides
- generic regex drops U+0027 and U+2019 so curly-quote names slug correctly
- consolidates duplicate-key `SLUG_OVERRIDES` apostrophe entries
- regression tests for curly-apostrophe Farfetch'd/Sirfetch'd
- `assembleObtainFile` merges PokéAPI-first, PokemonDB fallback, trades appended
- empty versions derive `evolve` → `egg` → `transfer` (default forms only)
- identical versions fold into one `ObtainGame` row per version group
- `bun` resolves module aliases from `$cwd/tsconfig.json`, not the
  referenced `tsconfig.app.json` project
- without it, `scripts/*.mts` importing `src/` modules that use `@/`
  fail to resolve under `bun`
- `scripts/build-obtain-data.mts`: PokéAPI + PokemonDB + Bulbapedia → `public/obtain/{id}.json`
- disk cache in `scripts/.cache/obtain/` (gitignored), per-host throttling, `--only` flag
- completeness gate fails the run if any default species file is missing
- lazy-fetches `obtain/{id}.json` under `BASE_URL` when enabled
- module cache + inflight dedupe, mirrors `useApiDetail`
- validates payload with `isObtainFile` guard
- generation groups collapsible, current gen expanded by default
- method tags tinted via palette vars; level/rate/condition chips per entry
- loading and `OBTAIN DATA UNAVAILABLE` states
- `public/obtain/{id}.json` for every species and variety
- output of `scripts/build-obtain-data.mts` against PokéAPI/PokemonDB/Bulbapedia
- `Section` gains optional `onToggle`; obtain data fetches on first open
- section sits between EVOLUTION and MOVES, `defaultOpen` false
- `count` shows number of game rows once loaded
- `prettyCondition` maps slug families: `time-`/`weather-`/`season-` strip, `slot2-` → `GBA:`, swarm/radar/radio toggles, den rarity/rating, coins
- chip style matches method tags: solid border, `0.68rem`, letter-spacing
- test fixture uses real dataset slugs (`time-night`, `weather-intense-sun`)
- skip derivation entirely when a version already has an `unavailable` entry
- forbid the EGG fallback in `NO_BREEDING_GROUPS` (`red-blue`, `yellow`,
  `lets-go-pikachu-lets-go-eevee`, `legends-arceus`) — EVOLVE still allowed
- `LIMITED_DEX_GROUPS` (LGPE, Legends: Arceus) emit `unavailable` /
  "Not in this game's Pokédex" instead of any derived row when a species
  has zero entries there
- drop the Bulbapedia NPC trade append when the version's PokéAPI entries
  already include a `trade` method (richer npc-trade encounter slots)
- update `obtainAssemble.test.ts` for the new gating: egg case now pins
  `gold-silver`, `red-blue` falls to `transfer`, plus new cases for
  unavailable-suppresses-derivation, limited-dex no-data, and trade dedupe
- drop derived EGG/EVOLVE rows that were stacking on top of `unavailable`
  entries (1,743 rows previously double-counted)
- remove EGG fallback rows from gen-1 and limited-dex groups (`red-blue`,
  `yellow`, `lets-go-pikachu-lets-go-eevee`, `legends-arceus`) where
  breeding isn't actually available
- replace derived rows with `unavailable` / "Not in this game's Pokédex"
  for species absent from the LGPE/Legends: Arceus regional dex, and drop
  duplicate NPC trade rows already covered by PokéAPI's `npc-trade` slots
- `useObtainData` now derives its returned `data` from `state.data.pokemonId
  === pokemonId`, falling back to the module cache — navigating with the
  section closed no longer leaks the previous Pokémon's game count into
  `PokemonCard`'s `HOW TO OBTAIN` count
- `ObtainMethods` treats `enabled && !data && !error` as loading too, so
  there's no one-frame "OBTAIN DATA UNAVAILABLE" flash before the fetch
  effect's first state update lands
- `<ObtainMethods key={pokemon.id} .../>` in `PokemonCard` resets expansion
  state per Pokémon instead of carrying over the previous one's open gens
- default-expand the first gen present when `currentGen` isn't in the file
  (regional forms like Alolan Vulpix report gen 1 but their obtain file
  starts at gen 7) instead of expanding nothing
- `tsconfig.json` now also carries the `@/` mapping (bun script resolution)
- `CLAUDE.md` said three files must stay in sync; it's four
- method tags and condition chips now pull colors from `TYPE_COLORS`
  (same palette/glow pattern as the type pills in `PokemonCard.tsx`)
  instead of the flat 4-tint set — the section reads far less
  green-and-grey
- condition chips get a category icon (◔ time, ✿ season, ☂︎/☀︎/❄︎/⚡︎
  weather, ≈ rods, ★ raid den, ⚑ story progress, ⇄ trade, ◎ GBA slot,
  ✧ other), rendered with the U+FE0E text-presentation selector so
  they never flip to colored emoji
- rename the `special` method tag label to `OTHER` (display only —
  `special` stays the data value)
- expand `prettyCondition`/`CONDITION_LABELS` with plain-English glosses
  for the overworld/wanderer/spot/ambush/berry-tree/honey-tree/headbutt/
  safari-zone/mount-item/save-data condition families so no chip is left
  unexplained
- add a compact, collapsed-by-default `LEGEND` disclosure above the gen
  list, glossing every method tag and condition icon; hides the native
  `<details>` marker so the custom ▶/▼ is the only disclosure indicator
- add `VG_REGION` overrides for version groups whose home region differs
  from their generation's default (FireRed/LeafGreen, HeartGold/SoulSilver,
  Omega Ruby/Alpha Sapphire, Let's Go, BDSP, Legends: Arceus) — gen headers
  now only show a region when every game under them shares it, and each
  `GameRow` labels its own region when it's an outlier (fixes BDSP/Legends:
  Arceus showing under "GEN VIII · GALAR", etc.)
- gen header shows all covered regions: `GEN VIII · GALAR / SINNOH / HISUI`
- per-game region labels kept for remake/spin-off rows
- tabs are region names (`KANTO`…`PALDEA` + `HISUI`), no gen numbers
- BDSP joins Sinnoh, Legends: Arceus gets its own Hisui tab, remakes go home
- per-row region labels and their CSS dropped — the tab already says it
- `legends-arceus` groups under Sinnoh; `HISUI` tab removed
- its rows read `LEGENDS ARCEUS · ANCIENT SINNOH`
- `HISUI · ANCIENT SINNOH` tab sits between Sinnoh and Unova
- row-level ancient-Sinnoh suffix and its CSS dropped
- `.prettierignore` skips `public/obtain/` — minified JSON is intentional
- unblocks `pre-push`'s `format:check` over 1,351 dataset files
@netlify

netlify Bot commented Jul 31, 2026

Copy link
Copy Markdown

Deploy Preview for thepokemax ready!

Name Link
🔨 Latest commit 99e756f
🔍 Latest deploy log https://app.netlify.com/projects/thepokemax/deploys/6a6cf075d48fa0000896080d
😎 Deploy Preview https://deploy-preview-5--thepokemax.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@codebend3r
codebend3r merged commit 7b8ea6e into main Jul 31, 2026
6 checks passed
@codebend3r
codebend3r deleted the obtainment-methods branch July 31, 2026 19:01
codebend3r added a commit that referenced this pull request Aug 22, 2026
- PR bodies drifted in shape across `#3`, `#5`, and `#6` — bullets in two,
  bold-lead paragraphs in the third, section names picked per PR
- skill fixes the contract: lede, `## What changed`, `## Verification`,
  `## Notes`, with per-subsystem sections when the change spans several
- bullets are the default form; prose is confined to the lede
- title rules mirror the commit-subject style already in `git log`
- `--body-file` is required, not preferred: backticks inside a
  double-quoted `--body` string are executed by the shell
- carries `CLAUDE.md`'s zero-AI-attribution rule into PR titles and bodies
codebend3r added a commit that referenced this pull request Aug 22, 2026
- both skills previously banned prefixes outright, matching the 60 commits
  measured at the time — that rule is now reversed by convention
- `PMX: ` is mandatory on every hand-written commit subject and PR title
- form follows the sister repo's `CJR:` convention: code, colon, one space
- pokemax capitalizes the first word after the prefix; `codebender-portfolio`
  lowercases it, so the two repos differ past the colon
- version bumps take the prefix too — `PMX: 0.3.4`
- `(#5)` squash suffixes still come from GitHub, never typed by hand
- this subject is the first commit written under the new rule
codebend3r added a commit that referenced this pull request Aug 22, 2026
- PR bodies drifted in shape across `#3`, `#5`, and `#6` — bullets in two,
  bold-lead paragraphs in the third, section names picked per PR
- skill fixes the contract: lede, `## What changed`, `## Verification`,
  `## Notes`, with per-subsystem sections when the change spans several
- bullets are the default form; prose is confined to the lede
- title rules mirror the commit-subject style already in `git log`
- `--body-file` is required, not preferred: backticks inside a
  double-quoted `--body` string are executed by the shell
- carries `CLAUDE.md`'s zero-AI-attribution rule into PR titles and bodies
codebend3r added a commit that referenced this pull request Aug 22, 2026
- both skills previously banned prefixes outright, matching the 60 commits
  measured at the time — that rule is now reversed by convention
- `PMX: ` is mandatory on every hand-written commit subject and PR title
- form follows the sister repo's `CJR:` convention: code, colon, one space
- pokemax capitalizes the first word after the prefix; `codebender-portfolio`
  lowercases it, so the two repos differ past the colon
- version bumps take the prefix too — `PMX: 0.3.4`
- `(#5)` squash suffixes still come from GitHub, never typed by hand
- this subject is the first commit written under the new rule
codebend3r added a commit that referenced this pull request Aug 22, 2026
* PMX: Drop always-true clause from `encountersToEntries`

- `METHOD_MAP` has no entry mapping to `'special'`
- so `method === 'special'` already implies `!METHOD_MAP[slug]`
- second clause could never be false when the first was true

* PMX: Make `isObtainFile` validate the shape its consumers index

- derive `ObtainMethod` from a new `OBTAIN_METHODS` list — one vocabulary
- guard now checks `games[].gen`/`versionGroup`/`versions`/`entries`
- rejects entries whose `method` is not a known method
- previously `ObtainMethods.tsx` indexed `METHOD_LABEL` on unvalidated strings
- verified all 1351 files in `public/obtain/` still pass

* PMX: Give the region model one owner in `generations.ts`

- new `REGIONS` + `REGION_OF_VERSION_GROUP` map every version group to its setting
- `GAMES_BY_REGION` now derives from `REGIONS` instead of restating it
- `ObtainMethods.tsx` drops `VG_REGION`, `REGION_ORDER`, `REGION_LABELS`
- kills the `getGen(gen).region` fallback, which was wrong for every remake
  and was the reason `VG_REGION` had to exist as a patch table
- Hisui's note comes from `REGIONS`, not a one-entry label override
- rename `crt-obtain-gen`/`crt-obtain-gen-toggle` — the rows are regions, not gens
- new `regions.test.ts` pins coverage and ordering against `GENERATIONS`

* PMX: Collapse the obtain loading contract into one status union

- `useObtainData` returns `ObtainState` — `idle` / `loading` / `error` / `ready`
- the enabled-but-not-yet-started frame now reports `loading` from the hook
- drops the compensating `loading || (enabled && !data && !error)` in the view
- drops the stale-id reconciliation — `settled.id` scopes failures to their id
- `ObtainMethods` takes `state` + `currentGen`, down from five props
- `enabled` no longer passed twice from `PokemonCard.tsx`
- ready branch split into `ObtainRegions` so the data is non-null by type
- loading/idle tests move to `useObtainData.test.ts` where the inputs live

* PMX: Drop the nullable expansion state from `ObtainRegions`

- `Set<string> | null` existed only because the default region was unknown
  until the file loaded — inside `ObtainRegions` it is known at mount
- plain `useState<Set<string>>` with a lazy initializer replaces it
- `toggle` no longer closes over a render-scoped `expanded` fallback
- not routed through `useExpandedRegions` on purpose: its persisted value
  would carry one Pokémon's open region onto the next

* PMX: Fold the condition tables into one `CONDITION_FAMILIES` model

- `CONDITION_PREFIXES`, `conditionMeta`'s if-chain, and `LEGEND_CONDITIONS`
  all dispatched on the same slug families — a new family meant three edits
- one table now carries prefix, icon, color, label strip, and legend gloss
- `LEGEND_CONDITIONS` derives from it instead of restating icon/color by hand
- `RODS` deleted — `ROD_METHODS` exported from `@/obtain/pokeapi` and shared
  by the build parser and the chip renderer
- diffed old vs new over all 341 condition slugs in `public/obtain/` — identical
- new test pins one chip per family, including the unknown-slug fallback

* PMX: Move the obtain display vocabulary into `src/obtain/labels.ts`

- `ObtainMethods.tsx` was 410 lines, ~250 of them static label tables
- `METHOD_LABEL`, `METHOD_COLOR`, `CONDITION_*`, `LEGEND_*`, `levelRate`,
  `prettyCondition`, `conditionMeta` now live beside the domain they describe
- component drops to 176 lines of rendering
- `regionLabel` takes `RegionMeta` instead of an ad-hoc object shape
- `LEGEND_CONDITIONS` uses `flatMap` so it narrows without `?? ''` padding
- new `obtainLabels.test.ts` pins label/color/gloss coverage to `OBTAIN_METHODS`

* PMX: Add a `create-pull-request` skill so PRs share one format

- PR bodies drifted in shape across `#3`, `#5`, and `#6` — bullets in two,
  bold-lead paragraphs in the third, section names picked per PR
- skill fixes the contract: lede, `## What changed`, `## Verification`,
  `## Notes`, with per-subsystem sections when the change spans several
- bullets are the default form; prose is confined to the lede
- title rules mirror the commit-subject style already in `git log`
- `--body-file` is required, not preferred: backticks inside a
  double-quoted `--body` string are executed by the shell
- carries `CLAUDE.md`'s zero-AI-attribution rule into PR titles and bodies

* PMX: Point `CLAUDE.md` at the `create-pull-request` skill

- line 13 claimed `pokemax-commit-format` governed "PR titles and bodies
  too" — two skills owning PR format is the drift the new skill removes
- that bullet now scopes to commit messages; a new bullet hands PR titles
  and bodies to `create-pull-request`
- both still carry the zero-AI-attribution rule, so narrowing one bullet
  does not open a gap

* PMX: Add the missing `pokemax-commit-format` skill, repo-local

- `CLAUDE.md` has mandated this skill since `e1a3761` but no file existed
  at the path it named, so the rule was unenforceable
- rules derived by measuring the last 60 commits, not invented: 188 body
  bullets, 0 ending in a period, 0 prose lines, subject median 53 chars
- lands in `.claude/skills/` beside `create-pull-request` rather than
  `~/.claude/skills/` — pokemax skills belong to the repo
- `CLAUDE.md` path updated to match; the `Don't` section reference needed
  no change since it names the skill without a path

* PMX: Cut duplicate CI runs and pin the toolchain in both workflows

- `ci.yml` ran on `push: branches: ['**']` and `pull_request`, so every
  commit on a PR branch triggered two identical full runs
- push now scopes to `main`; `pull_request` covers branch work
- `bun-version: latest` made CI non-deterministic — a bun release could
  break a green tree with no repo change; both workflows pin `1.4.0`,
  matching the local toolchain and the `.nvmrc`/`engines` pinning style
- `actions/cache@v4` on `~/.bun/install/cache`, keyed by `bun.lock`
- `ci.yml` gains `permissions: contents: read`; it inherited the repo
  default and needs no write scope
- `timeout-minutes` on all three jobs so a hung runner cannot burn minutes
- `workflow_dispatch` added to `ci.yml` for manual re-runs
- concurrency group keys on `github.workflow` too, so `ci.yml` and a future
  workflow on the same ref cannot cancel each other
- deploy keeps its own `test` step on purpose — `workflow_dispatch` can
  ship a commit CI never gated

* PMX: Require the `PMX:` repo-code prefix on commits and PRs

- both skills previously banned prefixes outright, matching the 60 commits
  measured at the time — that rule is now reversed by convention
- `PMX: ` is mandatory on every hand-written commit subject and PR title
- form follows the sister repo's `CJR:` convention: code, colon, one space
- pokemax capitalizes the first word after the prefix; `codebender-portfolio`
  lowercases it, so the two repos differ past the colon
- version bumps take the prefix too — `PMX: 0.3.4`
- `(#5)` squash suffixes still come from GitHub, never typed by hand
- this subject is the first commit written under the new rule

* PMX: Stop `.husky/pre-commit` failing docs-only commits

- `oxfmt` exits 2 when no staged file is a type it formats, printing to
  stderr and leaving stdout empty
- husky runs hooks under `set -e`, so the bare `unformatted=$(…)`
  assignment inherited that 2 and aborted the commit
- any commit staging only `.md`, `.svg`, or `.png` was blocked
- `|| true` on the assignment restores the intended behavior: no
  formattable files means nothing to reformat, so the hook exits 0
- latent since the hook was written; surfaced once `bun install` recreated
  `.husky/_` in this worktree and the hooks began running at all

* PMX: Rename `pokemax-commit-format` to `commit-format`

- the skill lives in the pokemax repo, so the repo name in the skill name
  was redundant — `.claude/skills/commit-format/`
- frontmatter `name:` and the `# Commit format` heading follow the path
- `CLAUDE.md` and `AGENTS.md` updated in both the Do and Don't sections
- `AGENTS.md` pointed at `~/.Codex/skills/pokemax-commit-format/SKILL.md`,
  which never existed; it now names the repo-local file like `CLAUDE.md`
- `docs/superpowers/plans/2026-07-31-obtainment-methods.md` still cites the
  old name and is left alone — it records what was true when it was written
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant